﻿package org.zengrong.cvsfile {
	import flash.utils.Dictionary;
	import org.zengrong.utils.StringUtil;

	/**
	 * @author Marco Müller / http://shorty-bmc.com
	 * @author zrong(zrongzrong@gmail.com) 2012-02-21 修改
	 * @see http://rfc.net/rfc4180.html RFC4180
	 * @see http://csvlib.googlecode.com csvlib
	 */
	public class CsvCode {
		public static const FORMATED_TYPE_OBJECT_ARRAY:String = 'Array';
		public static const FORMATED_TYPE_DICTIONARY:String = 'Dictionary';
		private var _fieldSeperator:String;      //分隔符
		private var _fieldEnclosureToken:String; //双引号
		private var _recordsetDelimiter:String;  //'\n'

		private var _csvString:String;
		private var _header:Array;
		private var _data:Array;
		private var _formatedType:String;
		private var _formatedData:Object;
		private var _embededHeader:Boolean;
		private var _headerOverwrite:Boolean;
		private var _sortField:*;
		private var _sortSequence:String;
		/**
		 * @param $str 要解析的CSV数据
		 */
		public function CsvCode($str:String = null){
			_fieldSeperator = ',';
			_fieldEnclosureToken = '"';
			_recordsetDelimiter = FileEnding.UNIX; //\n
			_embededHeader = true;
			_headerOverwrite = false;
			
			if ($str)
				decode($str);
		}
		
		/**
		 * 将字符串作为CSV进行解析
		 * @param $str
		 * @return 返回一个解析好的CSV对象
		 */
		public static function parse($str:String):CsvCode {
			var __csv:CsvCode = new CsvCode($str);
			return __csv;
		}

		// -> Public methods
		/**
		 * 在CSV中获取一条记录
		 */
		public function getRecordSet(index:int):Array {
			if (dataHasValues)
				return data[index]
			else
				return null
		}

		/**
		 * 在CSV中增加一条记录
		 */
		public function addRecordSet(recordset:Array, index:* = null):void {
			if (!dataHasValues)
				data = new Array();
			if (!index && index != 0)
				data.push(recordset);
			else
				data.splice(index, 0, recordset)
		}

		/**
		 * 根据指定的范围删除记录
		 * data.splice(startIndex, endIndex)
		 */
		public function deleteRecordSet(startIndex:int, endIndex:int = 1):Boolean {
			if (dataHasValues && startIndex < data.length && endIndex > 0)
				return data.splice(startIndex, endIndex)
			else
				return false
		}

		/**
		 * 
		 */
		public function search(needle:*, removeDuplicates:Boolean = true):Array {
			var result:Array = new Array()
			for each (var i:Array in data){
				if (needle is Array){
					for each (var j:String in needle)
						if (i.indexOf(String(j)) >= 0)
							result.push(i)
				} else if (i.indexOf(String(needle)) >= 0){
					result.push(i)
				}
			}
			if (removeDuplicates && result.length > 2)
				var k:int = result.length - 1;
			while (k--){
				var l:int = result.length;
				while (--l > k)
					if (result[k] == result[l])
						result.splice(l, 1)
			}
			return result
		}

		/*对CSV数据排序*/
		public function sort(fieldNameOrIndex:* = 0, sequence:String = 'ASC'):void {
			_sortSequence = sequence;
			if (headerHasValues && header.indexOf(fieldNameOrIndex) >= 0)
				_sortField = header.indexOf(fieldNameOrIndex);
			else
				_sortField = fieldNameOrIndex;
			if (dataHasValues)
				_data.sort(sort2DArray);
		}

		/**
		 * 解析传递的CSV字符串
		 * @param $str 要解析的CSV字符串
		 */
		public function decode($str:String):void {
			_csvString = $str;
			var __count:int = 0;
			var _result:Array = new Array();
			var __csvData:Array = _csvString.split(recordsetDelimiter);
			
			for (var i:int = 0; i < __csvData.length; i++){
				if (!Boolean(__count % 2))
					_result.push(__csvData[i]);
				else
					_result[_result.length - 1] += __csvData[i];
				__count += StringUtil.count(__csvData[i], fieldEnclosureToken);
			}
			_result = _result.filter(isNotEmptyRecord)
			_result.forEach(fieldDetection);
			if ((embededHeader && headerOverwrite) || (embededHeader && headerHasValues))
				_result.shift();
			else if (embededHeader)
				_header = _result.shift();
			_data = _result;
		}

		/**
		 * 将本地保存的数组转换成CSV字符串
		 */
		public function encode():void {
			_csvString = '';
			if (headerHasValues && header.length > 0){
				embededHeader = true
				data.unshift(header);
			}
			if (dataHasValues){
				for each (var recordset:Array in data)
					_csvString += recordset.join(fieldSeperator) + recordsetDelimiter;
			}
		}

		// -> private methods
		/**
		 * 检测域
		 */
		private function fieldDetection(element:*, index:int, arr:Array):void {
			var count:uint = 0;
			var result:Array = new Array();
			var tmp:Array = element.split(fieldSeperator);
			for (var i:uint = 0; i < tmp.length; i++){
				if (!Boolean(count % 2))
					result.push(StringUtil.trimList(tmp[i]));
				else
					result[result.length - 1] += fieldSeperator + tmp[i];
				count += StringUtil.count(tmp[i], fieldEnclosureToken);
			}
			arr[index] = result
		}

		private function sort2DArray(a:Array, b:Array):Number {
			var n:int = 0
			var r:int = _sortSequence == 'ASC' ? -1 : 1;
			if (String(a[_sortField]) < String(b[_sortField]))
				n = r
			else if (String(a[_sortField]) > String(b[_sortField]))
				n = -r
			else
				n = 0
			return n;
		}

		private function isNotEmptyRecord(element:*, index:int, arr:Array):Boolean {
			return Boolean(StringUtil.trimList(element));
		}

		/**
		 * 将CSV转换成对象列表
		 * @param $save 是否将解析后的结果保存在formatedData变量中
		 */
		public function toObjectArray($save:Boolean = false):Array {
			var _objarray:Array = [];
			var _unit:Object = null;
			for (var i:uint = 0; i < data.length; i++){
				_unit = {};
				for (var j:uint = 0; j < header.length; j++){
					_unit[header[j]] = data[i][j];
				}
				_objarray[i] = _unit;
			}
			if ($save){
				_formatedType = FORMATED_TYPE_OBJECT_ARRAY;
				formatedData = _objarray;
			} else {
				_formatedData = null;
				formatedData = null;
			}
			return _objarray;
		}

		/**
		 * 将列表导出成字典格式
		 * @param $id 提供字典的id使用哪个key
		 * @param $save 是否将解析后的结果保存在formatedData变量中
		 */
		public function toDictionary($id:String = null, $save:Boolean = false):Dictionary {
			var _dict:Dictionary = new Dictionary(false);
			var _unit:Object = null;
			var _idIndex:int = -1;
			for (var k:uint = 0; k < header.length; k++){
				//如果提供了id，并且在CSV的header中确实有这个id，就使用这个id作为这个字典的索引
				if ($id && $id == header[k]){
					_idIndex = k;
					break;
				}
			}
			for (var i:uint = 0; i < data.length; i++){
				_unit = {};
				for (var j:uint = 0; j < header.length; j++){
					_unit[header[j]] = data[i][j];
				}
				//若有id，使用id作为键名，否则使用数字作为键名
				if (_idIndex > -1)
					_dict[data[i][_idIndex]] = _unit;
				else
					_dict[i] = _unit;
			}
			if ($save){
				_formatedType = FORMATED_TYPE_DICTIONARY;
				formatedData = _dict;
			} else {
				_formatedData = null;
				formatedData = null;
			}
			return _dict;
		}

		/**
		 * 显示CSV的结构
		 */
		public function toString():String {
			var _result:String = 'data:Array -> [\r'
			for (var i:int = 0; i < data.length; i++){
				_result += '\t[' + i + ']:Array -> [\r'
				for (var j:uint = 0; j < data[i].length; j++)
					_result += '\t\t[' + j + ']:String -> ' + data[i][j] + '\r'
				_result += ('\t]\r')
			}
			_result += ']\r'
			return _result;
		}
		
		public function get formatedType():String {
			return _formatedType;
		}

		public function set formatedType($value:String):void {
			_formatedType = $value;
		}

		/**
		 * 保存使用toObjectArray或者toDictionary解析之后的数据格式
		 */
		public function get formatedData():Object {
			if (!_formatedType)
				return null;
			return _formatedData;
		}

		/**
		 * @private
		 */
		public function set formatedData($value:Object):void {
			_formatedData = $value;
		}

		public function get fieldSeperator():String {
			return _fieldSeperator
		}

		public function get fieldEnclosureToken():String {
			return _fieldEnclosureToken
		}

		public function get recordsetDelimiter():String {
			return _recordsetDelimiter
		}

		public function get embededHeader():Boolean {
			return _embededHeader
		}

		public function get headerOverwrite():Boolean {
			return _headerOverwrite
		}

		public function get header():Array {
			return _header
		}

		public function get data():Array {
			return _data;
		}

		public function get headerHasValues():Boolean {
			var check:Boolean
			try {
				if (_header.length > 0)
					check = true
			} catch (e:Error){
				check = false
			} finally {
				return check
			}
		}

		public function get dataHasValues():Boolean {
			var check:Boolean
			try {
				if (data.length > 0)
					check = true
			} catch (e:Error){
				check = false
			} finally {
				return check
			}
		}

		/*返回转换的字符*/
		public function get csvString():String {
			return _csvString;
		}

		public function set csvString($value:String):void {
			_csvString = $value;
		}

		// -> setter
		public function set fieldSeperator(value:String):void {
			_fieldSeperator = value
		}

		public function set fieldEnclosureToken(value:String):void {
			_fieldEnclosureToken = value
		}

		public function set recordsetDelimiter(value:String):void {
			_recordsetDelimiter = value
		}

		public function set embededHeader(value:Boolean):void {
			_embededHeader = value
		}

		public function set headerOverwrite(value:Boolean):void {
			_headerOverwrite = value
		}

		public function set data($value:Array):void {
			_data = $value;
		}

		public function set header(value:Array):void {
			if ((!embededHeader && !headerHasValues) || (!embededHeader && headerHasValues && headerOverwrite) || headerOverwrite)
				_header = value
		}
	}
}